package gdingtalk

import (
	"github.com/astaxie/beego/httplib"
	"github.com/astaxie/beego/logs"
	"github.com/tidwall/gjson"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const OapiHost = "https://oapi.dingtalk.com"
const ApiHost = "https://api.dingtalk.com"

type Request struct {
	method       string
	path         string
	queryParams  url.Values
	bodyParam    map[string]interface{}
	headerParams map[string]string
	// 自动带入access_token,所需的corpId
	autoAccessTokenCorpId string
}

// NewRequest
func NewRequest() *Request {
	req := Request{}
	req.queryParams = make(url.Values)
	req.bodyParam = make(map[string]interface{})
	req.headerParams = make(map[string]string)
	return &req
}

// Post 请求
func Post() *Request {
	req := NewRequest()
	req.SetMethod("post")
	return req
}

// Get 请求
func Get() *Request {
	req := NewRequest()
	req.SetMethod("get")
	return req
}

// Put 请求
func Put() *Request {
	req := NewRequest()
	req.SetMethod("put")
	return req
}

// Delete 请求
func Delete() *Request {
	req := NewRequest()
	req.SetMethod("delete")
	return req
}

// SetMethod 设置请求类型，支持post,get
// sample: SetMethod("POST") 或 SetMethod("post") 都是合法
func (req *Request) SetMethod(method string) *Request {
	lowerMethod := strings.ToLower(method)
	req.method = lowerMethod
	return req
}

// SetPath 设置请求路
// sample:  SetPath("/get_jsapi_ticket")
func (req *Request) SetPath(path string) *Request {
	req.path = path
	return req
}

// SetQueryParam 设置query参数
func (req *Request) SetQueryParam(values url.Values) *Request {
	req.queryParams = values
	return req
}

// AddQueryParam 增加query参数
func (req *Request) AddQueryParam(key, value string) *Request {
	req.queryParams.Add(key, value)
	return req
}

// AddHeaderParam 新增header参数
// sample:
// AddHeaderParam("name", "sven")
func (req *Request) AddHeaderParam(key, value string) *Request {
	req.headerParams[key] = value
	return req
}

// SetBodyParam 设置post body参数
// sample:
//
//	SetBodyParam(map[string]interface{}{
//	    "name": "sven",
//	    "age": 18
//	})
func (req *Request) SetBodyParam(body map[string]interface{}) *Request {
	req.bodyParam = body
	return req
}

// SetBodyParam 新增post body参数
// sample:
// AddBodyParam("name", "sven")
func (req *Request) AddBodyParam(key string, value interface{}) *Request {
	req.bodyParam[key] = value
	return req
}

// AutoAccessToken 自动带入accessToken，需要传入corpId
func (req *Request) AutoAccessToken(corpId string) *Request {
	req.autoAccessTokenCorpId = corpId
	return req
}

// Do 发送http请求
func (req *Request) Do() Response {
	if req.autoAccessTokenCorpId != "" {
		accessToken, _ := gDingTalk.GetAccessToken(req.autoAccessTokenCorpId)
		req.AddQueryParam("access_token", accessToken)
	}
	var url string
	queryString := req.queryParams.Encode()
	if queryString == "" {
		url = OapiHost + req.path
	} else {
		url = OapiHost + req.path + "?" + req.queryParams.Encode()
	}
	var httpReq *httplib.BeegoHTTPRequest
	if req.method == "post" {
		httpReq = httplib.Post(url)
		httpReq.JSONBody(req.bodyParam)
	} else if req.method == "get" {
		httpReq = httplib.Get(url)
	} else if req.method == "put" {
		httpReq = httplib.Put(url)
		httpReq.JSONBody(req.bodyParam)
	} else if req.method == "delete" {
		httpReq = httplib.Delete(url)
		httpReq.JSONBody(req.bodyParam)
	} else {

	}
	// 判断是否要增加header
	if len(req.headerParams) > 0 {
		for k, v := range req.headerParams {
			httpReq.Header(k, v)
		}
	}
	res, _ := httpReq.String()
	response := NewResponse(res)
	if response.IsSuccess() == false {
		logs.Error("dingding request %s err: %s", url, response.GetErrmsg())
	}
	return response
}

// Do2 发送http请求，钉钉新版接口
func (req *Request) Do2() Response2 {
	if req.autoAccessTokenCorpId != "" {
		accessToken, _ := gDingTalk.GetAccessToken(req.autoAccessTokenCorpId)
		req.AddHeaderParam("x-acs-dingtalk-access-token", accessToken)
	}

	var url string
	queryString := req.queryParams.Encode()
	if queryString == "" {
		url = ApiHost + req.path
	} else {
		url = ApiHost + req.path + "?" + req.queryParams.Encode()
	}
	var httpReq *httplib.BeegoHTTPRequest
	if req.method == "post" {
		httpReq = httplib.Post(url)
		httpReq.JSONBody(req.bodyParam)
	} else if req.method == "get" {
		httpReq = httplib.Get(url)
	} else if req.method == "put" {
		httpReq = httplib.Put(url)
		httpReq.JSONBody(req.bodyParam)
	} else if req.method == "delete" {
		httpReq = httplib.Delete(url)
		httpReq.JSONBody(req.bodyParam)
	} else {

	}
	// 判断是否要增加header
	if len(req.headerParams) > 0 {
		for k, v := range req.headerParams {
			httpReq.Header(k, v)
		}
	}
	res, _ := httpReq.Response()
	response := NewResponse2(res)
	if response.IsSuccess() == false {
		logs.Error("dingding request2 %s err: %s", url, response.GetErrmsg())
	}
	return response
}

type Response struct {
	response gjson.Result
}

// NewResponse 实例化response
func NewResponse(res string) Response {
	return Response{
		response: gjson.Parse(res),
	}
}

// IsSuccess 判断返回值是否成功
func (res *Response) IsSuccess() bool {
	if res.response.Get("errcode").Exists() && res.response.Get("errcode").Int() == 0 {
		return true
	} else {
		return false
	}
}

// GetErrcode 返回钉钉errcode
func (res *Response) GetErrcode() int64 {
	return res.response.Get("errcode").Int()
}

// GetBody 返回钉钉业务数据
func (res *Response) GetBody(path ...string) gjson.Result {
	if len(path) == 0 {
		return res.response
	}
	return res.response.Get(path[0])
}

// GetErrmsg 返回钉钉错误信息
func (res *Response) GetErrmsg() string {
	if res.response.Get("errmsg").Exists() {
		return res.response.Get("errmsg").String()
	} else {
		return res.response.String()
	}
}

type Response2 struct {
	res  *http.Response
	body gjson.Result
}

// NewResponse2 实例化response
func NewResponse2(res *http.Response) Response2 {
	defer res.Body.Close()
	str, _ := ioutil.ReadAll(res.Body)
	return Response2{
		res:  res,
		body: gjson.ParseBytes(str),
	}
}

// IsSuccess 判断返回值是否成功
func (res *Response2) IsSuccess() bool {
	if res.res.StatusCode == 200 {
		// 还会根据业务码判断
		// 业务码类似，{"errcode":404,"errmsg":"请求的URI地址不存在"}
		if res.body.Get("errcode").Exists() && res.body.Get("errcode").Int() != 0 {
			return false
		}
		return true
	} else {
		return false
	}
}

// GetErrcode 返回钉钉errcode
func (res *Response2) GetErrcode() int64 {
	return int64(res.res.StatusCode)
}

// GetBody 返回钉钉业务数据
func (res *Response2) GetBody(path ...string) gjson.Result {
	if len(path) == 0 {
		return res.body
	}
	return res.body.Get(path[0])
}

// GetErrmsg 返回钉钉错误信息
func (res *Response2) GetErrmsg() string {
	if res.body.Get("errmsg").Exists() {
		return res.body.Get("errmsg").String()
	} else {
		return res.body.String()
	}
}
